package cn.buk.tms.dao;

import cn.buk.tms.entity.misc.DeliveryNote;
import cn.buk.tms.entity.misc.DeliveryNote_;
import cn.buk.tms.entity.misc.OperationLog;
import cn.buk.tms.entity.order.OrderNoInfo;
import cn.buk.tms.sc.SearchFlightOrderCriteria;
import cn.buk.tms.common.constant.TmsOrderConstant;
import cn.buk.common.util.DateUtil;
import jakarta.persistence.criteria.*;
import org.springframework.stereotype.Component;

import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static cn.buk.tms.common.constant.TmsOrderConstant.OPERATING_OBJECT_DELIVERY;

/**
 * 配送单DAO
 * @author gezhi
 */
@Component
@Transactional
public class DeliveryNoteDaoImpl extends AbstractDao implements DeliveryNoteDao {

  @Override
  public int createDeliveryNote(DeliveryNote o) {

    final int enterpriseId = o.getEnterpriseId();

    em.persist(o);

    //日志
    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_DELIVERY);
    log.setOrderId(o.getId());
    log.setOperator(o.getOperator());
    log.setContent("创建配送单");
    em.persist(log);

    //写关联信息到对应的业务订单
    if (OrderNoInfo.ORDER_FLIGHT.equalsIgnoreCase(o.getDeliveredOrderType())) {
      //对应于机票订单
      int retCode = em.createQuery("update FlightOrder o set o.deliveryNoteNo = :noteNo "
          + "where o.enterpriseId = :enterpriseId and o.orderNo = :orderNo")
          .setParameter("enterpriseId", enterpriseId)
          .setParameter("orderNo", o.getDeliveredOrderNo())
          .setParameter("noteNo", o.getOrderNo())
          .executeUpdate();

      if (retCode != 1) {
        throw new RuntimeException("Failed to set delivery note No. int flight order.");
      }
    }

    return o.getId();
  }

  @Override
  public List<DeliveryNote> searchDeliveryNotes(int enterpriseId,
      SearchFlightOrderCriteria sc) {

    List<DeliveryNote> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<DeliveryNote> cq = cb.createQuery(DeliveryNote.class);
    Root<DeliveryNote> root = cq.from(DeliveryNote.class);
    root.alias("o");

    var predicates0 = buildPredicates_searchDeliveryNotes(enterpriseId, sc, cb, root);

    /**
     *计算根据条件查询得出的数据总数
     */
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<DeliveryNote> countRoot = countQuery.from(DeliveryNote.class);
    countRoot.alias("o");

    var predicates1 = buildPredicates_searchDeliveryNotes(enterpriseId, sc, cb, countRoot);

    // count items
    countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

    List<Order> orderByes = new ArrayList<>();
    orderByes.add(cb.desc(root.get("id")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      sc.getPage().setRowCount(maxResults);

      results = em.createQuery(cq)
          .setFirstResult(sc.getPage().getFirstPosition())
          .setMaxResults(sc.getPage().getPageSize())
          .getResultList();

    }

    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  private static List<Predicate> buildPredicates_searchDeliveryNotes(int enterpriseId, SearchFlightOrderCriteria sc, CriteriaBuilder cb, Root<DeliveryNote> root) {
    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(DeliveryNote_.enterpriseId), enterpriseId);
    predicates.add(predicate);

    if (sc.getBeginDate() != null && sc.getEndDate() != null) {
      predicate = cb.greaterThanOrEqualTo(root.get(DeliveryNote_.deliveryDate), sc.getBeginDate());
      predicate = cb.and(predicate, cb.lessThanOrEqualTo(root.get(DeliveryNote_.deliveryDate), sc.getEndDate()));
      predicates.add(predicate);
    } else if (sc.getBeginDate() != null) {
      predicate = cb.greaterThanOrEqualTo(root.get(DeliveryNote_.deliveryDate), sc.getBeginDate());
      predicates.add(predicate);
    } else if (sc.getEndDate() != null) {
      predicate = cb.lessThanOrEqualTo(root.get(DeliveryNote_.deliveryDate), sc.getEndDate());
      predicates.add(predicate);
    }

    if (sc.getOrderNo().length()  > 0) {
      //订单号
      predicate = cb.equal(root.get(DeliveryNote_.orderNo), sc.getOrderNo());
      predicates.add(predicate);
    }
    return predicates;
  }

  @Override
  public List<DeliveryNote> searchDeliveryNoteByOrderNo(int enterpriseId,
      String orderNo) {
    SearchFlightOrderCriteria sc = new SearchFlightOrderCriteria();
    sc.setOrderNo(orderNo);

    return searchDeliveryNotes(enterpriseId, sc);
  }

  @Override
  public DeliveryNote getDeliveryNoteById(int orderId) {
    return em.find(DeliveryNote.class, orderId);
  }

  @Override
  public int processDeliveryNote(int enterpriseId, String username, int orderId) {
    int retCode = em.createQuery("update DeliveryNote o set o.status = :newStatus, o.processor = :processor, o.lastUpdate = :lastUpdate "
        + "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
        .setParameter("id", orderId)
        .setParameter("enterpriseId", enterpriseId)
        .setParameter("oldStatus", 0)
        .setParameter("newStatus", 1)
        .setParameter("processor", username)
        .setParameter("lastUpdate", DateUtil.getCurDateTime())
        .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to update delivery note status.");
    }

    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_DELIVERY);
    log.setOrderId(orderId);
    log.setOperator(username);
    log.setContent("处理配送单");

    em.persist(log);

    return retCode;
  }

  @Override
  public int finishDeliveryNote(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update DeliveryNote o set o.status = :newStatus, o.lastUpdate = :lastUpdate "
        + "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
        .setParameter("id", orderId)
        .setParameter("enterpriseId", enterpriseId)
        .setParameter("oldStatus", 1)
        .setParameter("newStatus", 2)
        .setParameter("lastUpdate", DateUtil.getCurDateTime())
        .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to update delivery note status.");
    }

    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_DELIVERY);
    log.setOrderId(orderId);
    log.setOperator(username);
    log.setContent("完成：" + remark);

    em.persist(log);

    return retCode;
  }

  @Override
  public int cancelDeliveryNote(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update DeliveryNote o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
        "where o.id = :id and o.enterpriseId = :enterpriseId and o.status in (:oldStatus1, :oldStatus2)")
        .setParameter("id", orderId)
        .setParameter("enterpriseId", enterpriseId)
        .setParameter("oldStatus1", TmsOrderConstant.ORDER_STATUS_WAITING)
        .setParameter("oldStatus2", TmsOrderConstant.ORDER_STATUS_PROCESSING)
        .setParameter("newStatus", TmsOrderConstant.ORDER_STATUS_CANCELED)
        .setParameter("lastUpdate", DateUtil.getCurDateTime())
        .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("To cancel DeliveryNote failed.");
    }

    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_DELIVERY);
    log.setOperator(username);
    log.setOrderId(orderId);
    log.setContent("取消： " + remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public int updateDeliveryNoteDeliveryDate(int enterpriseId, String username, int orderId,
      Date deliveryDate) {
    int retCode = em.createQuery("update DeliveryNote o set o.deliveryDate = :deliveryDate, o.lastUpdate = :lastUpdate "
        + "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
        .setParameter("id", orderId)
        .setParameter("enterpriseId", enterpriseId)
        .setParameter("oldStatus", 0)
        .setParameter("deliveryDate", deliveryDate)
        .setParameter("lastUpdate", DateUtil.getCurDateTime())
        .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to update delivery note status.");
    }

    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_DELIVERY);
    log.setOrderId(orderId);
    log.setOperator(username);

    log.setContent("配送日期修改为：" + DateUtil.formatDate(deliveryDate, "yyyy-MM-dd"));

    em.persist(log);

    return retCode;
  }

  @Override
  public List<OperationLog> searchDeliveryNoteHistory(int enterpriseId, int orderId) {
    return searchOrderHistory(enterpriseId, OPERATING_OBJECT_DELIVERY, orderId);
  }
}
